package de.hb.bv2.u03;

import de.hb.bv2.u03.ContourSet;
import de.hb.bv2.u03.ContourTracer;

import ij.process.ImageProcessor;
import ij.gui.GenericDialog;
import ij.gui.Line;

import java.util.ArrayList;

/**
 * In dieser Klasse sind alle Methoden zur dritten
 * Uebung enthalten.
 */
public final class Gruppe03_U03_Logic 
{		
	// Enum's zur Beschreibung der Position der Punkte 
	// im Bezug auf das Doppelkreuz.
	public enum Punktlage
	{
		PUNKT_A,
		PUNKT_B,
		VORNE,
		HINTEN,
		ZWISCHEN_A_UND_B,
		LINKS_VORNE,
		RECHTS_VORNE,
		LINKS_VON_B,
		RECHTS_VON_B,
		LINKS_ZWISCHEN_A_UND_B,
		RECHTS_ZWISCHEN_A_UND_B,
		LINKS_VON_A,
		RECHTS_VON_A,
		LINKS_HINTEN,
		RECHTS_HINTEN,
		ERROR
	};
	
	// Enum's zur Beschreibung der Position der Geraden
	// im Bezug auf das Doppelkreuz.
	public enum BA23_Relation
	{
		FRONT_LEFT,
		FRONT_MIDDLE,
		FRONT_RIGHT,
		FRONT_OVERLAPS_LEFT,
		FRONT_OVERLAPS_MIDDLE_LEFT,
		FRONT_OVERLAPS_MIDDLE_RIGHT,
		FRONT_OVERLAPS_RIGHT,
		FRONT_CONTAINS_LEFT,
		FRONT_CONTAINS_RIGHT,
		DURING_LEFT,
		CONTAINS_LEFT,
		ID,
		CONTAINS_RIGHT,
		DURING_RIGHT,
		BACK_CONTAINS_LEFT,
		BACK_CONTAINS_RIGHT,
		BACK_OVERLAPS_LEFT,
		BACK_OVERLAPS_MIDDLE_LEFT,
		BACK_OVERLAPS_MIDDLE_RIGHT,
		BACK_OVERLAPS_RIGHT,
		BACK_LEFT,
		BACK_MIDDLE,
		BACK_RIGHT,
		_
	};
	
	
    /**
     * Dies ist die Hauptmethode zur Berechnung des Extent
     * Wertes. In ihr werden die Konturen extrahiert, 
     * polygonisiert, sortiert und der Methode Extent_Funktion()
     * zur Bestimmung des Wertes uebergeben.
     *  
     * @param ip Ursprungsbild
     */
	public static void Extent(final ImageProcessor ip) 
	{
		// Kontur instanziieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		// Kontur vereinfachen.
		Konturvereinfachung(ip, cs);
		
		// Kontur sortieren.
		Kontursortierung(ip, cs.outerContours, true);
		Kontursortierung(ip, cs.innerContours, false);
			
		// Hier wird fuer die aeusseren und inneren Konturen
		// der Extent Wert getrennt berechnet.
		System.out.println("\nÄußere Konturen:");
		Extent_Funktion(cs.outerContours); 
			
		System.out.println("Innere Konturen:");
		Extent_Funktion(cs.innerContours); 
		
		// Zum Schluss noch die polygonisierten Konturen
		// zeichnen.
		Konturzeichnen(ip, cs);
	}
	
	
    /**
     * In dieser Methode wird die Liste mit den Konturen 
     * durchlaufen und dabei fuer jede Kontur die BA23
     * Matrix mit Hilfe der Methode Berechnung_BA23_Matrix()
     * bestimmt. Durch die Methode Berechnung_Extentwerte()
     * werden fuer eine Kontur die Extent Werte berechnet.
     * Anschliessend kann aus diesen Werten der gesammte Extent
     * Wert fuer eine Kontur bestimmt werden.
     * 
	 * @param contours Eine Liste mit Konturen fuer die jeweils 
     *                 der Extent Wert bestimmt werden soll.
     */
	public static void Extent_Funktion(ArrayList<Contour> contours) 
	{
		// Matrix fuer die BA23 Relationen einer Kontur.
		BA23_Relation[][] ba23_array = null;
		
		// Array, in der die Extent Werte einer Kontur gespeichert
		// werden.
		int[] extent_array = null;
		
		// In dieser Schleife werden alle der Methode uebergebenen
		// Konturen bearbeitet.
		for(int i = 0; i < contours.size(); i++) 
		{	
			// Zuerst wird fuer die aktuelle Kontur die BA23
			// Matrix berechnet.
			ba23_array = Berechnung_BA23_Matrix(contours.get(i));
			
			// Nun werden fuer die BA23 Matrix die Extent Werte
			// berechnet.
			extent_array = Berechnung_Extentwerte(ba23_array);
			
			// Dieser Wert speichert den gesamten Extent Wert der
			// aktuellen Kontur.
			double polygonalExtent = 0.0;
			
			
			// Hier werden die einzelnen Extent Werte ausgegeben.
			// Auch werden hier die Extent Werte aufsummiert.
			System.out.println("Einzelne Extentwerte:");
			
			for(int k = 0; k < extent_array.length; k++) 
			{
				polygonalExtent += extent_array[k];
			
				System.out.print(extent_array[k] + " | ");
			}
			
			// Zum Schluss kann der gesammte Extent Wert einer Kontur
			// bestimmt und ausgegeben werden.
			System.out.println("\n\nPolygonaler Extentwert:");
			System.out.print(polygonalExtent/(extent_array.length*12));
			
			System.out.print("\n\n");
		}
	}
	
	
    /**
     * In dieser Methode wird fuer eine BA23 Matrix ein Array mit 
     * Extent Werten berechnet. Dabei wird die Matrix spaltenweise
     * durchlaufen und fuer jede Spalte der Extent Wert bestimmt.
     * 
     * @param ba23_array Die BA23 Matrix, fuer die die Extent Werte
     *                   berechnet werden sollen.
     *                   
     * @return Ein Array mit den Extent Werten einer Kontur.
     */
	public static int[] Berechnung_Extentwerte(BA23_Relation[][] ba23_array) 
	{	
		// Ein Array, in das die Extent Werten einer Kontur gespeichert
		// werden sollen.
		int[] extent = new int[ba23_array.length];
				
		// Hier wird die BA23 Matrix spaltenweise durchlaufen und fuer
		// jede Spalte der Extent Wert berechnet.
		for(int j = 0; j < ba23_array[0].length; j++) 
		{
			// Eine Matrix zur Speicherung der BA12 Relationen.
			// Dient jedoch nur zur Speicherung der Positionen,
			// die die Relationen einnehmen.
			boolean[] ba12 = new boolean[12];
			
			// Jetzt wird an der aktuellen Spalte die Matrix
			// zeilenweise durchlaufen und dabei die BA12
			// Positionen bestimmt.
			for(int i = 0; i < ba23_array.length; i++) 
			{
				Berechnung_BA12_Matrix(ba12, ba23_array[i][j]);
			}
						
			// Zum Schluss wird die Anzahl an Positionseintraegen
			// in der Matrix bestimmt. Dieser Wert ist der
			// Extent Wert fuer die aktuelle Spalte und wird 
			// in dem Rueckgabearray gespeichert.
			int anzahlPunkte = 0;
			
			for(int k = 0; k < ba12.length; k++) 
			{
				if(ba12[k]) anzahlPunkte++;
			}
			
			extent[j] = anzahlPunkte;
		}
		
		return extent;
	}
	

    /**
     * Dies ist die Hauptmethode zur Berechnung des Extremum
     * Wertes. In ihr werden die Konturen extrahiert, 
     * polygonisiert, sortiert und der Methode Extremum_Funktion()
     * zur Bestimmung des Wertes uebergeben.
     *  
     * @param ip Ursprungsbild
     */
	public static void Extremum(final ImageProcessor ip) 
	{
		// Kontur instanziieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		// Kontur vereinfachen.
		Konturvereinfachung(ip, cs);
					
		// Kontur sortieren.
		Kontursortierung(ip, cs.outerContours, true);
		Kontursortierung(ip, cs.innerContours, false);
					
		// Hier wird fuer die aeusseren und inneren Konturen
		// der Extremum Wert getrennt berechnet.
		System.out.println("\nÄußere Konturen:");
		Extremum_Funktion(cs.outerContours); 
			
		System.out.println("Innere Konturen:");
		Extremum_Funktion(cs.innerContours); 
		
		// Zum Schluss noch die polygonisierten Konturen
		// zeichnen.
		Konturzeichnen(ip, cs);
	}
	
	
    /**
     * In dieser Methode wird die Liste mit den Konturen 
     * durchlaufen und dabei fuer jede Kontur die BA23
     * Matrix mit Hilfe der Methode Berechnung_BA23_Matrix()
     * bestimmt. Durch die Methode Berechnung_Extremumwerte()
     * werden fuer eine Kontur die Extremum Werte berechnet.
     * Anschliessend kann aus diesen Werten der gesammte Extremum
     * Wert fuer eine Kontur bestimmt werden.
     * 
	 * @param contours Eine Liste mit Konturen fuer die jeweils 
     *                 der Extremum Wert bestimmt werden soll.
     */
	public static void Extremum_Funktion(ArrayList<Contour> contours) 
	{
		// Matrix fuer die BA23 Relationen einer Kontur.
		BA23_Relation[][] ba23_array = null;
		
		// Array, in der die Extremum Werte einer Kontur gespeichert
		// werden.
		int[] extremum_array = null;
		
		// In dieser Schleife werden alle der Methode uebergebenen
		// Konturen bearbeitet.
		for(int i = 0; i < contours.size(); i++) 
		{	
			// Zuerst wird fuer die aktuelle Kontur die BA23
			// Matrix berechnet.
			ba23_array = Berechnung_BA23_Matrix(contours.get(i));
			
			// Nun werden fuer die BA23 Matrix die Extremum Werte
			// berechnet.
			extremum_array = Berechnung_Extremumwerte(ba23_array);
			
			// Dieser Wert speichert den gesamten Extremum Wert der
			// aktuellen Kontur.
			double polygonalExtremum = 0.0;
			
			// Hier werden die einzelnen Extremum Werte ausgegeben.
			// Auch werden hier die Extremum Werte aufsummiert.
			System.out.println("Einzelne Extremumwerte:");
			
			for(int k = 0; k < extremum_array.length; k++) 
			{
				polygonalExtremum += extremum_array[k];
			
				System.out.print(extremum_array[k] + " | ");
			}
			
			// Zum Schluss kann der gesammte Extremum Wert einer Kontur
			// bestimmt und ausgegeben werden.
			System.out.println("\n\nPolygonaler Extremumwert:");
			System.out.print(polygonalExtremum/extremum_array.length);
			
			System.out.print("\n\n");
		}
	}
	
	
    /**
     * In dieser Methode wird fuer eine BA23 Matrix ein Array mit 
     * Extremum Werten berechnet. Dabei wird die Matrix spaltenweise
     * durchlaufen und fuer jede Spalte der Extremum Wert bestimmt.
     * 
     * @param ba23_array Die BA23 Matrix, fuer die die Extremum Werte
     *                   berechnet werden sollen.
     *                   
     * @return Ein Array mit den Extremum Werten einer Kontur.
     */
	public static int[] Berechnung_Extremumwerte(BA23_Relation[][] ba23_array) 
	{	
		// Ein Array, in das die Extremum Werten einer Kontur gespeichert
		// werden sollen.
		int[] extremum = new int[ba23_array.length];
			
		// Hier wird die BA23 Matrix spaltenweise durchlaufen und fuer
		// jede Spalte der Extremum Wert berechnet.
		for(int j = 0; j < ba23_array[0].length; j++) 
		{
			// Eine Matrix zur Speicherung der BA12 Relationen.
			// Dient jedoch nur zur Speicherung der Positionen,
			// die die Relationen einnehmen.
			boolean[] ba12 = new boolean[12];
			
			// Jetzt wird an der aktuellen Spalte die Matrix
			// zeilenweise durchlaufen und dabei die BA12
			// Positionen bestimmt.
			for(int i = 0; i < ba23_array.length; i++) 
			{
				Berechnung_BA12_Matrix(ba12, ba23_array[i][j]);
			}
				
			// Zum Schluss wir die Anzahl an Positionseintraegen
			// in der Matrix bestimmt. Dabei werden die ersten
			// 4 Eintraege der Matrix gesondert gezaehlt, da dies
			// die Positionen links von der Bezugsgeraden sind.
			int anzahlPunkte = 0;
			int anzahlPunkteLinks = 0;
			
			for(int k = 0; k < ba12.length; k++) 
			{
				if(ba12[k])
				{
					anzahlPunkte++;
					
					if(k <= 4)
					{
						anzahlPunkteLinks++;
					}
				}
			}
			
			// Sind alle Eintraege links von der Bezugsgeraden,
			// so betraget der Extremum Wert 1, ansonsten 0.
			if(anzahlPunkte == anzahlPunkteLinks)
			{
				extremum[j] = 1;
			}	
		}
		
		return extremum;
	}
	
	
    /**
     * Dies ist die Hauptmethode zur Berechnung des Curvature
     * Wertes. In ihr werden die Konturen extrahiert, 
     * polygonisiert, sortiert und der Methode Curvature_Funktion()
     * zur Bestimmung des Wertes uebergeben.
     * 
     * @param ip Ursprungsbild
     */
	public static void Curvature(final ImageProcessor ip) 
	{
		// Kontur instanziieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		// Kontur vereinfachen.
		Konturvereinfachung(ip, cs);
					
		// Kontur sortieren.
		Kontursortierung(ip, cs.outerContours, true);
		Kontursortierung(ip, cs.innerContours, false);
					
		// Hier wird fuer die aeusseren und inneren Konturen
		// der Curvature Wert getrennt berechnet.
		System.out.println("\nÄußere Konturen:");
		Curvature_Funktion(cs.outerContours); 
			
		System.out.println("Innere Konturen:");
		Curvature_Funktion(cs.innerContours); 
		
		// Zum Schluss noch die polygonisierten Konturen
		// zeichnen.
		Konturzeichnen(ip, cs);
	}
	
	
    /**
     * In dieser Methode wird die Liste mit den Konturen 
     * durchlaufen und dabei fuer jede Kontur die BA23
     * Matrix mit Hilfe der Methode Berechnung_BA23_Matrix()
     * bestimmt. Durch die Methode Berechnung_Curvaturewerte()
     * werden fuer eine Kontur die Curvature Werte berechnet.
     * Anschliessend kann aus diesen Werten der gesammte Curvature
     * Wert fuer eine Kontur bestimmt werden.
     * 
	 * @param contours Eine Liste mit Konturen fuer die jeweils 
     *                 der Curvature Wert bestimmt werden soll.
     */
	public static void Curvature_Funktion(ArrayList<Contour> contours) 
	{
		// Matrix fuer die BA23 Relationen einer Kontur.
		BA23_Relation[][] ba23_array = null;
		
		// Array, in der die Curvature Werte einer Kontur gespeichert
		// werden.
		int[] curvature_array = null;
		
		// In dieser Schleife werden alle der Methode uebergebenen
		// Konturen bearbeitet.
		for(int i = 0; i < contours.size(); i++) 
		{	
			// Zuerst wird fuer die aktuelle Kontur die BA23
			// Matrix berechnet.
			ba23_array = Berechnung_BA23_Matrix(contours.get(i));
			
			// Nun werden fuer die BA23 Matrix die Curvature Werte
			// berechnet.
			curvature_array = Berechnung_Curvaturewerte(ba23_array);
			
			// Dieser Wert speichert den gesamten Curvature Wert der
			// aktuellen Kontur.
			double polygonalCurvature = 0.0;
			
			// Hier werden die einzelnen Curvature Werte ausgegeben.
			// Auch werden hier die Curvature Werte aufsummiert.
			System.out.println("Einzelne Curvaturewerte:");
			
			for(int k = 0; k < curvature_array.length; k++) 
			{
				polygonalCurvature += (double)((double)1/(double)curvature_array[k]);
			
				System.out.print(curvature_array[k] + " | ");
			}
			
			// Zum Schluss kann der gesammte Curvature Wert einer Kontur
			// bestimmt und ausgegeben werden.
			System.out.println("\n\nPolygonaler Curvaturewert:");
			
			polygonalCurvature = (double)((double)1-
					        (double)((double)((double)polygonalCurvature/
							(double)curvature_array.length)));
			System.out.print(polygonalCurvature);
			
			System.out.print("\n\n");
		}
	}
	
	
    /**
     * In dieser Methode wird fuer eine BA23 Matrix ein Array mit 
     * Curvature Werten berechnet. Dabei wird die Matrix spaltenweise
     * durchlaufen und fuer jede Spalte der Curvature Wert bestimmt.
     * 
     * @param ba23_array Die BA23 Matrix, fuer die die Curvature Werte
     *                   berechnet werden sollen.
     *                   
     * @return Ein Array mit den Curvature Werten einer Kontur.
     */
	public static int[] Berechnung_Curvaturewerte(BA23_Relation[][] ba23_array) 
	{	
		// Ein Array, in das die CurvatureWerten einer Kontur gespeichert
		// werden sollen.
		int[] curvature = new int[ba23_array.length];
			
		// Hier wird die BA23 Matrix spaltenweise durchlaufen und fuer
		// jede Spalte der Curvature Wert berechnet.
		for(int j = 0; j < ba23_array[0].length; j++) 
		{
			// Eine Matrix zur Speicherung der BA12 Relationen.
			// Dient jedoch nur zur Speicherung der Positionen,
			// die die Relationen einnehmen.
			boolean[] ba12_aktuell = new boolean[12];
			boolean[] ba12_vorgaenger = new boolean[12];
			boolean[] ba12_nachfolger = new boolean[12];
			
			// Jetzt wird an der aktuellen Spalte die Matrix
			// zeilenweise durchlaufen und dabei die BA12
			// Positionen bestimmt.
			for(int i = 0; i < ba23_array.length; i++) 
			{
				// Flag fuer die Identitaet.
				boolean ID = false;
				
				// Flag, der angibt, ob zwei BA12 Matrizen
				// invers zueinander sind.
				boolean str = false;
				
				// Flag, der angibt, ob es eine Ueberschneidung
				// an der Identitaet gibt.
				boolean intersectionID = false;
				
				// Flag, der angibt, ob eine Ueberschneidung gibt.
				boolean intersection = false;
				
				// Der Extentwert einer BA12 Matrix.
				int extentValue = 0;
				
				// Hier wird die BA12 Matrix fuer den aktuellen Zeileneintrag
				// berechnet.
				Berechnung_BA12_Matrix(ba12_aktuell, ba23_array[i][j]);
				
				// Hier werden die BA12 Matrizen fuer den vorherigen und
				// den nachfolgenden Zeileneintrag berechnet.
				if(i == 0)
				{
					Berechnung_BA12_Matrix(ba12_vorgaenger, ba23_array[ba23_array.length-1][j]);
				} else {
					Berechnung_BA12_Matrix(ba12_vorgaenger, ba23_array[i-1][j]);
				}
				
				if(i == (ba23_array.length-1))
				{
					Berechnung_BA12_Matrix(ba12_nachfolger, ba23_array[0][j]);
				} else {
					Berechnung_BA12_Matrix(ba12_nachfolger, ba23_array[i+1][j]);
				}
				
				// Hier wird der Extentwert fuer die aktuelle BA12 Matrix
				// bestimmt.
				extentValue = getExtent(ba12_aktuell);
				
				// Sind Zeile und Spalte identisch, so wird gerade ein
				// Matrixeintrag mit der Identitaet betrachtet.
				if(i == j) ID = true;
				
				// Hier wird ermittelt, ob der vorherige und der nachfolgende
				// Eintrag invers zueinander sind.
				str = str(ba12_vorgaenger, ba12_nachfolger);
				
				// Hier wird ermittelt, ob der vorherige und der nachfolgende
				// Eintrag sich ueberschneiden.
				intersectionID = intersection(ba12_vorgaenger, ba12_nachfolger);
				
				// Hier wird ermittelt, ob der aktuelle und der nachfolgende
				// Eintrag sich ueberschneiden.
				intersection = intersection(ba12_aktuell, ba12_nachfolger);
				
				// Hier wird der Curvaturewert fuer den aktuellen Zeileneintrag
				// ermittelt. Hierbei werden die 5 Faelle unterschieden.
				// 1. Fall: 0 wenn ri = Id && (str(ri) && int(ri-1; ri+1))
				if(ID && str && intersectionID)
				{
					//curvature[j] += 0;
				} else {
					// 2. Fall: 1 wenn ri = Id && (str(ri) || int(ri-1; ri+1))
					if(ID && (str || intersectionID))
					{
						curvature[j] += 1;
					} else {
						// 3. Fall: 2 wenn ri = Id
						if(ID)
						{
							curvature[j] += 2;
						} else {
							// 4. Fall: theta(sigma(ri))-1 wenn int(ri; ri+1)
							if(intersection)
							{
								curvature[j] += (extentValue-1);
							} else { // 5. Fall: theta(sigma(ri)) else
								curvature[j] += extentValue;
							}
						}
					}
				}	
			}
		}
		
		return curvature;
	}
	
	
    /**
     * In dieser Methode wird fuer eine BA23 Matrix ein Array mit 
     * Curvature Werten berechnet. Dabei wird die Matrix spaltenweise
     * durchlaufen und fuer jede Spalte der Curvature Wert bestimmt.
     * 
     * @param ba12Die BA12 Matrix, fuer die der Extentwert berechnet
     *                werden soll.
     *                   
     * @return Der Extentwert der BA12 Matrix.
     */
	public static int getExtent(boolean[] ba12)
	{
		int extent = 0;
		
		// In dieser Schleife wird der Extentwert
		// ermittelt.
		for(int i = 0; i < ba12.length; i++)
		{
			// Ist ein Eintrag in der Matrix auf true,
			// so wird der Extentwert um Eins erhoeht.
			if(ba12[i]) extent++;
		}
		
		return extent;
	}
	
	
    /**
     * In dieser Methode ermiitelt, ob zwei BA12 Matrizen
     * invers zueinander sind.
     * 
     * @param ba12_vorgaenger Die erste BA12 Matrix.
     * @param ba12_nachfolger Die zweite BA12 Matrix.
     *                   
     * @return True, wenn beide Matrizen invers zueinander sind.
     *         Ansonsten false.
     */
	public static boolean str(boolean[] ba12_vorgaenger, boolean[] ba12_nachfolger)
	{
		// Zu Beginn wird eine Matrix invertiert.
		boolean[] ba12_inverse = new boolean[12];
		
		for(int i = 0; i < ba12_inverse.length; i++)
		{
			ba12_inverse[((i+6)%ba12_inverse.length)] = ba12_vorgaenger[i];
		}
		
		// Jetzt wird ermittelt, wie viele Eintrage identisch sind.
		int identical = 0;
		
		for(int i = 0; i < ba12_inverse.length; i++)
		{
			if(ba12_inverse[i] == ba12_nachfolger[i]) identical++;
		}
			
		// Sind alle 12 Eintrage identisch, so sind beide Matrizen
		// invers zueinander.
		if(identical == 12)
		{
			return true;
		} else {
			return false;
		}	
	}
	
	
    /**
     * In dieser Methode ermiitelt, ob sich zwei BA12 Matrizen
     * ueberschneiden.
     * 
     * @param ba12_r1 Die erste BA12 Matrix.
     * @param ba12_r2 Die zweite BA12 Matrix.
     *                   
     * @return True, wenn eine Ueberschneidung vorliegt. 
     *         Ansonsten false.
     */
	public static boolean intersection(boolean[] ba12_r1, boolean[] ba12_r2)
	{
		// Ermitteln, wie viele Eintraege identisch sind.
		// Die Eintrage muessen true sein.
		int intersections = 0;
		
		for(int i = 0; i < ba12_r1.length; i++)
		{
			if(ba12_r1[i] && ba12_r2[i]) intersections++;
		}
		
		// Ist wenigstens ein Eintrag identisch und auf true,
		// so wird ein true zurueckgegeben. Ansonsten false.
		if(intersections > 0)
		{
			return true;
		} else {
			return false;
		}
	}
	
	
    /**
     * Methode zum Testen der BA23 Relations Funktion.
     * Hier wird die uebergende Kontur durchlaufen
     * und dabei die Relationen bestimmt.
     * 
     * @param ip Ursprungsbild
     * @param contour Die zu bearbeitende Kontur.
     * 
     * @return Die BA23 Relationen in einer Matrix
     *         gespeichert.
     */
	public static BA23_Relation[][] Berechnung_BA23_Matrix(Contour contour) 
	{	
		// Die Matrix, in der die BA23 Relationen gespeichert werden sollen.
		BA23_Relation[][] ba23_array = new BA23_Relation[contour.nodes.size()][contour.nodes.size()];
		
		// Ausgabe der Spaltenueberschriften (Bezugsgerade).
		for(int k = 0; k < contour.nodes.size(); k++) 
		{
			System.out.print(k);
			System.out.print((k+1)%contour.nodes.size() + " | ");
		}
		
		System.out.print("\n");
		
		// Hier wird die Kontur Segmentweise durchlaufen.
		for(int j = 0; j < contour.nodes.size() ; j++) 
		{
			// Ausgabe der Zeilenbeschriftung (zu betrachtende Gerade). 
			System.out.print(j);
			System.out.print((j+1)%contour.nodes.size() + " | ");
			
			// Hier wird die Kontur Segmentweise durchlaufen.
			for(int k = 0; k < contour.nodes.size() ; k++) 
			{
				// Ausgabe der bestimmten Relation.
				Enum<de.hb.bv2.u03.Gruppe03_U03_Logic.BA23_Relation> relation;
				
				// Speichern der Relation in der Matrix.
				relation = BA23_Relation(contour.nodes.get(k), contour.nodes.get((k+1)%contour.nodes.size()), 
						contour.nodes.get(j), contour.nodes.get((j+1)%contour.nodes.size()));
				
				ba23_array[j][k] = (de.hb.bv2.u03.Gruppe03_U03_Logic.BA23_Relation) relation;
				
				System.out.print(relation + " | ");
			}
			
			System.out.print("\n");
		}
		
		System.out.print("\n");
		
		return ba23_array;
	}
	
	
	/**
	 * Diese Methode sorgt dafuer, dass BA23 Relationen, die der
	 * Methode uebergeben werden, in BA12 Relationen umgewandelt
	 * werden. Die BA12 Matrix speichert jedoch keine Relationen,
	 * sondern nur die Positionen, die die Relationen einnehmen.
	 * 
	 * @param ba12 Die Matrix, in der die Positionen gespeichert
	 *             werden sollen.
	 * @param relation Die BA23 Relation, fuer die die Positionen 
	 *                 bestimmt werden sollen, die die Relation 
	 *                 einnimmt.
	 */
	public static void Berechnung_BA12_Matrix(boolean[] ba12, Enum<BA23_Relation> relation) 
	{
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_LEFT")) == 0)
		{
			ba12[0] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_MIDDLE")) == 0)
		{
			ba12[11] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_RIGHT")) == 0)
		{
			ba12[10] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_LEFT")) == 0)
		{
			ba12[1] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_MIDDLE_LEFT")) == 0)
		{
			ba12[0] = true;
			ba12[1] = true;
			ba12[2] = true;
			ba12[10] = true;
			ba12[11] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_MIDDLE_RIGHT")) == 0)
		{
			ba12[0] = true;
			ba12[8] = true;
			ba12[9] = true;
			ba12[10] = true;
			ba12[11] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_RIGHT")) == 0)
		{
			ba12[9] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_CONTAINS_LEFT")) == 0)
		{
			ba12[0] = true;
			ba12[1] = true;
			ba12[2] = true;
			ba12[3] = true;
			ba12[4] = true;
			ba12[10] = true;
			ba12[11] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "FRONT_CONTAINS_RIGHT")) == 0)
		{
			ba12[0] = true;
			ba12[6] = true;
			ba12[7] = true;
			ba12[8] = true;
			ba12[9] = true;
			ba12[10] = true;
			ba12[11] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "DURING_LEFT")) == 0)
		{
			ba12[2] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "CONTAINS_LEFT")) == 0)
		{
			ba12[0] = true;
			ba12[1] = true;
			ba12[2] = true;
			ba12[3] = true;
			ba12[4] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "CONTAINS_RIGHT")) == 0)
		{
			ba12[6] = true;
			ba12[7] = true;
			ba12[8] = true;
			ba12[9] = true;
			ba12[10] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "DURING_RIGHT")) == 0)
		{
			ba12[8] = true;
		}
				
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_CONTAINS_LEFT")) == 0)
		{
			ba12[0] = true;
			ba12[1] = true;
			ba12[2] = true;
			ba12[3] = true;
			ba12[4] = true;
			ba12[5] = true;
			ba12[6] = true;
		}
				
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_CONTAINS_RIGHT")) == 0)
		{
			ba12[4] = true;
			ba12[5] = true;
			ba12[6] = true;
			ba12[7] = true;
			ba12[8] = true;
			ba12[9] = true;
			ba12[10] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_LEFT")) == 0)
		{
			ba12[3] = true;
		}		
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_MIDDLE_LEFT")) == 0)
		{
			ba12[2] = true;
			ba12[3] = true;
			ba12[4] = true;
			ba12[5] = true;
			ba12[6] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_MIDDLE_RIGHT")) == 0)
		{
			ba12[4] = true;
			ba12[5] = true;
			ba12[6] = true;
			ba12[7] = true;
			ba12[8] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_RIGHT")) == 0)
		{
			ba12[7] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_LEFT")) == 0)
		{
			ba12[4] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_MIDDLE")) == 0)
		{
			ba12[5] = true;
		}
		
		if(relation.compareTo(Enum.valueOf(BA23_Relation.class, "BACK_RIGHT")) == 0)
		{
			ba12[6] = true;
		}
	}
	
	
    /**
     * Diese Methode transformiert alle inneren und aeusseren Konturen
     * in den Spektralbereich und verwendet bei der Ruecktransformation 
     * in den Ortsraum, die ersten k (anzahlFourierdeskriptoren) 
     * betragsmaessig groessten Fourierdeskriptoren.
     * 
     * @param ip Ursprungsbild
     */
	public static void Fourierdeskriptoren(final ImageProcessor ip) 
	{
		// Die Anzahl an Fourierdeskriptoren, die bei der Ruecktransformation
		// beruecksichtigt werden sollen
		int anzahlFourierdeskriptoren = 10;
		
		// Hier wird der GenericDialog instanziiert, um dem Bentutzer
		// die Eingabe eines Wertes fuer die Anzahl der 
		// Fourierdeskriptoren zu ermoeglichen.		
		GenericDialog gd = new GenericDialog("Fouriertransformation");
	
		// Ein Felder fuer den einzugebenden Parameter wird hinzugefuegt.		
		gd.addNumericField("Anzahl Fourierdeskriptoren: ", anzahlFourierdeskriptoren, 0);
	
		// Nun wird der Dialog angezeigt.		
		gd.showDialog();
		
		// Wert einlesen.
		anzahlFourierdeskriptoren = (int) gd.getNextNumber();
		
		// Wird auf abbrechen gedrueckt, wird die Methode beendet.
		if(gd.wasCanceled()) return;
		
		// Kontur instanziieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		
		// Zuerst das Bild komplett weiss faerben.
		for(int x = 0; x < ip.getWidth(); x++)
		{
			for(int y = 0; y < ip.getHeight(); y++)
			{
				ip.putPixel(x, y, 255);
			}
		}
		
		// Nun werden die auesseren Konturen einzelnd in den 
		// Spektralbereich transformiert und anschliessen wieder
		// in den Ortsraum zuruecktransformiert. Bei der Ruecktransformation
		// werden nur die ersten k (anzahlFourierdeskriptoren) betragsmaessig 
		// groessten Fourierdeskriptoren beruecksichtigt.
		for(int i = 0; i < cs.outerContours.size(); i++) 
		{
			// Nur wenn die Anzahl der Konturpunkte die Anzahl der
			// Fourierdeskriptoren uebersteigt oder gleich gross ist,
			// darf transformiert werden.
			if(cs.outerContours.get(i).nodes.size() >= anzahlFourierdeskriptoren)
			{
				// Die Konturpunkte der aktuellen Kontur als komplexe Zahl
				// repraesentiert. Die x-Koordinaten werden als Realteil und
				// die y-Koordinaten als Imaginaerteil gespeichert.
				Complex[] g = null;
				
				// Speicher fuer alle Fourierdeskriptoren.
				Complex[] G = null;
				
				// Hier werden die Konturpunkte als komplexe Zahlen 
				// gespeichert.
				g = Contour_To_Complex(cs.outerContours.get(i));
				
				// Nun werden diese komplexen Zahlen in den 
				// Spektralbereich transformiert. Fuer einen 
				// einzelnen Fourierdeskriptor, werden alle 
				// Konturpunkte bei der Berechnung beruecksichtigt.
				G = DFT(g, true, g.length);
							
				// Jetzt werden alle Fourierdeskriptoren betragsmaessig
				// absteigend sortiert.
				java.util.Arrays.sort(G);
			
				// Bei der Ruecktransformation in den Ortsraum werden nur
				// k (anzahlFourierdeskriptoren) Fourierdeskriptoren verwendet.
				// Da diese zuvor sortiert wurden, werden die k betragsmaessig
				// groessten Fourierdeskriptoren verwendet.
				g = DFT(G, false, anzahlFourierdeskriptoren);
				
				// Zum Schluss werden die zuruecktransformierten Konturpunkte
				// in das Bild eingezeichnet.
				Draw_Complex(ip, g, true);
			}
		}
		
		// Nun werden die innerenKonturen einzelnd in den 
		// Spektralbereich transformiert und anschliessen wieder
		// in den Ortsraum zuruecktransformiert. Bei der Ruecktransformation
		// werden nur die ersten k (anzahlFourierdeskriptoren) betragsmaessig 
		// groessten Fourierdeskriptoren beruecksichtigt.
		for(int i = 0; i < cs.innerContours.size(); i++) 
		{
			// Nur wenn die Anzahl der Konturpunkte die Anzahl der
			// Fourierdeskriptoren uebersteigt oder gleich gross ist,
			// darf transformiert werden.
			if(cs.innerContours.get(i).nodes.size() >= anzahlFourierdeskriptoren)
			{
				// Die Konturpunkte der aktuellen Kontur als komplexe Zahl
				// repraesentiert. Die x-Koordinaten werden als Realteil und
				// die y-Koordinaten als Imaginaerteil gespeichert.
				Complex[] g = null;
				
				// Speicher fuer alle Fourierdeskriptoren.
				Complex[] G = null;
				
				g = Contour_To_Complex(cs.innerContours.get(i));
				
				// Nun werden diese komplexen Zahlen in den 
				// Spektralbereich transformiert. Fuer einen 
				// einzelnen Fourierdeskriptor, werden alle 
				// Konturpunkte bei der Berechnung beruecksichtigt.
				G = DFT(g, true, g.length);
							
				// Jetzt werden alle Fourierdeskriptoren betragsmaessig
				// absteigend sortiert.
				java.util.Arrays.sort(G);
						
				// Bei der Ruecktransformation in den Ortsraum werden nur
				// k (anzahlFourierdeskriptoren) Fourierdeskriptoren verwendet.
				// Da diese zuvor sortiert wurden, werden die k betragsmaessig
				// groessten Fourierdeskriptoren verwendet.
				g = DFT(G, false, anzahlFourierdeskriptoren);
				
				// Zum Schluss werden die zuruecktransformierten Konturpunkte
				// in das Bild eingezeichnet.
				Draw_Complex(ip, g, false);
			}
		}
	}
	
	
    /**
     * Diese Methode zeichnet die Konturpunkte, die als komplexe 
     * Zahlen gespeichert sind.
     * 
     * @param ip Ursprungsbild
     * @param g Die Konturpunkte in komplexer Darstellung.
     * @param outerContour Flag der angibt, ob eine aeussere oder
     *                     eine innere Kontur gezeichnet werden soll.
     */
	public static void Draw_Complex(final ImageProcessor ip, Complex[] g, 
			boolean outerContour) 
	{						
		for(int i = 0; i < g.length; i++) 
		{		
			if(outerContour)
			{
				ip.putPixel((int)g[i].re, (int)g[i].im, 0);
			} else {
				ip.putPixel((int)g[i].re, (int)g[i].im, 127);
			}
		}
	}
	
	
    /**
     * Diese Methode speichert die Konturpunkte der uebergebenen Kontur
     * als komplexe Zahlen.
     * 
     * @param contour Die Kontur, deren Konturpunkte als komplexe Zahlen 
     *                gespeichert werden sollen.
     *                
     * @return Die Konturpunkte in komplexer Darstellung.
     */
	public static Complex[] Contour_To_Complex(Contour contour) 
	{
		// Speicher fuer die komplexen Zahlen. Da jeder Konturpunkt als
		// komplexe Zahl gespeichert werden soll, ist dieses Array genau so
		// gross, wie die Liste mit den Konturpunkten.
		Complex[] g = new Complex[contour.nodes.size()];
		
		// Hier findet die Speicherung statt.
		for(int i = 0; i < contour.nodes.size(); i++) 
		{
			g[i] = new Complex(contour.nodes.get(i).x, contour.nodes.get(i).y, i);
		}
		
		return g;
	}
	
	
    /**
     * Diese Methode implementiert die diskrete Fouriertransformation.
     * 
     * @param g Die komlexen Zahlen, die in den Spektralbereich oder in 
     *          den Ortsraum transformiert werden sollen.
     * @param forward Flag der angibt, ob die Fouriertransformation oder
     *                die inverse Fouriertransformation verwendet werden
     *                soll. 
     * @param K Die Anzahl an komplexen Zahlen die bei der Transformation
     *          bereucksichtigt werden sollen.
     *          Hintransformation: Anzahl an Konturpunkten.
     *          Ruecktransformation: Anzahl an Fourierdeskriptoren.
     *                
     * @return Hintransformation: Konturpunkten im Spektralbereich.
     *         Ruecktransformation: Konturpunkten im Ortsraum.
     */
	public static Complex[] DFT(Complex[] g, boolean forward, int K) 
	{
		// Die Lange des Arrays mit den komplexen Zahlen.
		int M = g.length;
		
		// Der Skalierungswert.
		double s = 1 / Math.sqrt(M);
			
		// Das Array, in das die Transformationsergebnisse gespeichert
		// werden sollen. 
		Complex[] G = new Complex[M];
		
		// Innerhalb dieser beiden for-Schleifen findet die 
		// Fouriertransformation bzw. die inverse Fouriertransformation
		// statt.
		// Hintransformation: Der Index m gibt an, welcher Fourierdeskriptor
		// gerade berechnet wird. Der Index u geht dabei alle Konturpunkte
		// durch und sorgt dafuer, dass fuer einen einzelnen Fourierdeskriptor
		// alle Konturpunkte beruecksichtigt werden.
		// Ruecktransformation: Der Index m gibt an, welcher Konturpunkt
		// gerade berechnet wird. Der Index u sorgt dafuer, dass die ersten K
		// betragsmaessig groessten Fourierdeskriptoren bei der Berechnung eines
		// einzelnen Konturpunktes beruecksichtigt werden. 
		for (int m = 0; m < M; m++) 
		{
			// Summe des Realteils.
			double sumRe = 0;
			
			// Summe des Imaginaerteils.
			double sumIm = 0;
			
			// Die Berechnung der Phase.
			double phim = 2 * Math.PI * m / M;
						
			// Hier werden bei der Hintransformation alle Konturpunkte bei
			// der Berechnung eines einzelnen Fourierdeskriptor beruecksichtigt.
			// Bei der Ruecktransformation werden nur die ersten K
			// betragsmaessig groessten Fourierdeskriptoren bei der Berechnung eines
			// einzelnen Konturpunktes beruecksichtigt.
			for (int u = 0; u < K; u++) 
			{
				// Real- bzw. Imaginaerteil der aktuellen komplexen Zahl.
				double gRe = g[u].re;
				double gIm = g[u].im;
				
				// Der Cosinus bzw. der Sinus der Frequenz w.
				double cosw = Math.cos(phim * u);
				double sinw = Math.sin(phim * u);
				
				// Bei der Ruecktransformation muss das Vorzeichen des 
				// Sinus geaendert werden. Auch die Position des 
				// Fourierdeskriptor ist in der komplexen Zahl gespeichert.
				if(!forward)
				{
					cosw = Math.cos(phim * g[u].pos);
					sinw = -Math.sin(phim * g[u].pos);
				}
								
				// Komplexe Multiplikation: [gRe + igIm] · [cos(w) + i sin(w)]
				sumRe += gRe * cosw + gIm * sinw;
				sumIm += gIm * cosw - gRe * sinw;
			}
			
			// Zum Schluss muss noch die Skalierung beruecksichtigt werden und das
			// Ergebnis in dem Rueckgabearray gespeichert werden.
			G[m] = new Complex(s * sumRe, s * sumIm, m);
		}
		
		return G;
	}
	
	
	
	
	// *** Methoden aus der Uebung 02 ***
	
	
	

    /**
     * Kontur zeichnen.
     * 
     * @param ip Ursprungsbild
     * @param cs Konturen, aufgeteilt in innere und aeussere Konturen.
     */
	public static void Konturzeichnen(final ImageProcessor ip, ContourSet cs) 
	{
		// Bild komplett weiss faerben
		for(int x = 0; x < ip.getWidth(); x++)
		{
			for(int y = 0; y < ip.getHeight(); y++)
			{
				ip.putPixel(x, y, 255);
			}
		}
		
		// Aeussere Konturen einzeichnen
		for(int i = 0; i < cs.outerContours.size(); i++) 
		{
			for(int j = 0; j < cs.outerContours.get(i).nodes.size()-1; j++) 
			{
				Line aussen = new Line(
						cs.outerContours.get(i).nodes.get(j).x,
						cs.outerContours.get(i).nodes.get(j).y,
						cs.outerContours.get(i).nodes.get(j+1).x,
						cs.outerContours.get(i).nodes.get(j+1).y);
				aussen.drawPixels(ip);
			}
			
			Line aussenLast = new Line(
					cs.outerContours.get(i).nodes.get(cs.outerContours.get(i).nodes.size()-1).x,
					cs.outerContours.get(i).nodes.get(cs.outerContours.get(i).nodes.size()-1).y,
					cs.outerContours.get(i).nodes.get(0).x,
					cs.outerContours.get(i).nodes.get(0).y);
			aussenLast.drawPixels(ip);
		}
		
		
		// Innere Konturen einzeichnen
		for(int i = 0; i < cs.innerContours.size(); i++) 
		{
			for(int j = 0; j < cs.innerContours.get(i).nodes.size()-1; j++) 
			{
				Line innen = new Line(
						cs.innerContours.get(i).nodes.get(j).x,
						cs.innerContours.get(i).nodes.get(j).y,
						cs.innerContours.get(i).nodes.get(j+1).x,
						cs.innerContours.get(i).nodes.get(j+1).y);
				innen.drawPixels(ip);
			}
			
			Line innenLast = new Line(
					cs.innerContours.get(i).nodes.get(cs.innerContours.get(i).nodes.size()-1).x,
					cs.innerContours.get(i).nodes.get(cs.innerContours.get(i).nodes.size()-1).y,
					cs.innerContours.get(i).nodes.get(0).x,
					cs.innerContours.get(i).nodes.get(0).y);
			innenLast.drawPixels(ip);
		}	
	}
	
	
	/**
	 * In dieser Methode wird durch einen Dialog abgefragt, auf wie viele
	 * Konturpunkte das Polygon reduziert werden soll. Mit diesem Wert
	 * und dem Eingabebild, wird dann die implementierte Funktion
	 * aufgerufen.
	 *  
	 * @param ip Ursprungsbild
	 */
	public static void Discrete_Curve_Evolution(final ImageProcessor ip) 
	{
		// Die Anzahl an Konturpunkte, auf die das Polygon reduziert 
		// werden soll.
		int sollPunkte = 10;
	
		// Hier wird der GenericDialog instanziiert, um dem Bentutzer
		// die Eingabe eines Wertes fuer sollPunkte zu ermoeglichen.		
		GenericDialog gd = new GenericDialog("Discrete Curve Evolution");
	
		// Ein Felder fuer den einzugebenden Parameter wird hinzugefuegt.		
		gd.addNumericField("Anzahl Konturpunkte: ", sollPunkte , 0);
	
		// Nun wird der Dialog angezeigt.		
		gd.showDialog();
		
		// Wert einlesen.
		sollPunkte = (int) gd.getNextNumber();
		
		// Wird auf abbrechen gedrueckt, wird die Methode beendet.
		if(gd.wasCanceled()) return;
		
		// Konturen extrahieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		// Hier wird die eigentliche Funktion aufgerufen.
		Discrete_Curve_Evolution_Funktion(ip, sollPunkte, cs.outerContours);
		Discrete_Curve_Evolution_Funktion(ip, sollPunkte, cs.innerContours);
		
		// Kontur zeichnen.
		Konturzeichnen(ip, cs);
	} 
	
	
	/**
	 * In dieser Methode wird das Polygon nach dem DCE-Verfahren reduziert.
	 * 
	 * @param ip Ursprungsbild
	 * @param sollPunkte Die Anzahl an Konturpunkte, auf die das Polygon 
	 *                   reduziert wird.
	 * @param contours Die zu bearbeitende Kontur.                  
	 */
	public static void Discrete_Curve_Evolution_Funktion(final ImageProcessor ip, 
			int sollPunkte, ArrayList<Contour> contours) 
	{				
		int istPunkte = 0; // Tatsaechliche Anzahl an Konturpunkte.
		
		// Hilfswerte, um die Position des Konturpunktes mit dem kleinsten Wert
		// zu finden.
		double globalMinimum = 0.0;
		double tempMinimum = 0.0;
		int indexMinimum = 0;
								
		// Hier werden die aeusseren Konturen durchlaufen.
		for(int i = 0; i < contours.size(); i++) 
		{
			// Die momentane Anzahl an Konturpunkte speichern.
			istPunkte = contours.get(i).nodes.size();
			
			// Solange die Kontur aus mehr Punkten als der Sollwert
			// besteht, werden Punkte geloescht. 
			while(istPunkte > sollPunkte) 
			{	
				// In dieser Schleife werden alle Konturpunkte analysiert.
				for(int j = 0; j < istPunkte; j++) 
				{
					// Hier werden zwei Geraden (3 Punkte) der Kostenfunktion 
					// uebergeben.
					tempMinimum = Kostenfunktion(
							contours.get(i).nodes.get(j%istPunkte), 
							contours.get(i).nodes.get((j+1)%istPunkte), 
							contours.get(i).nodes.get((j+2)%istPunkte));
					
					// Der erste Wert wird sofort als Minimum gespeichert.
					// Bei allen anderen muss ueberprueft werden, ob der 
					// temporaere Wert kleiner ist als der globale Wert.
					// Der Index des Punktes mit dem kleinsten Wert wird 
					// gespeichert.
					if(j == 0)
					{
						globalMinimum = tempMinimum;						
						indexMinimum = (j+1)%istPunkte;
					} else {
						if(Double.compare(tempMinimum, globalMinimum) < 0)
						{
							globalMinimum = tempMinimum;					
							indexMinimum = (j+1)%istPunkte;
						}
					}
				}
				
				// Hier wird der Konturpunkt mit dem gerinsgten Kostenwert
				// geloescht.
				contours.get(i).nodes.remove(indexMinimum);
				
				// Die aktuelle Anzahl an Konturpunkten speichern.
				istPunkte = contours.get(i).nodes.size();
			}
		}	
	} 
	
	
	/**
	 * Die Kostenfunktion fuer das DCE-Verfahren.
	 * 
	 * @param a Der Startpunkt der ersten Geraden.
	 * @param b Der Endpunkt der ersten Geraden, Startpunkt der 
	 *          zweiten Geraden.
	 * @param c Der Endpunkt der zweiten Geraden.
	 * 
	 * @return Der berechnete Kostenwert.
	 */
	public static double Kostenfunktion(Node a, Node b, Node c) 
	{
		// Differenzen der Punkte in x-Richtung.
		int tau1 = 0;
		int tau2 = 0;
		
		// Differenzen der Punkte in y-Richtung.
		int delta1 = 0;
		int delta2 = 0;
		
		// Die Laengen der beiden Geraden.
		double l1 = 0.0;
		double l2 = 0.0;
		
		// Der Winkel zwischen den beiden Geraden.
		double beta = 0.0;
		
		// Flaeche innerhalb der drei Punkte.
		double p_delta_abc = 0.0;
		
		// Der zu berechndende Kostenwert.
		double k = 0.0;
		
		// Differnezen in x-Richtung berechnen.
		tau1 = Math.abs(a.x-b.x);
		tau2 = Math.abs(b.x-c.x);
	
		// Differnezen in y-Richtung berechnen.
		delta1 = Math.abs(a.y-b.y);
		delta2 = Math.abs(b.y-c.y);
				
		// Laengen berechnen.
		l1 = (double)Math.sqrt(((double)Math.pow(tau1, 2) 
				+ (double)Math.pow(delta1, 2)));
		l2 = (double)Math.sqrt(((double)Math.pow(tau2, 2)
				+ (double)Math.pow(delta2, 2)));
			
		// Winkel berechnen.
		beta = (double)Math.atan((double)delta1/tau1) 
				+ (double)Math.atan((double)delta2/tau2);
		
		// Flaeche berechnen.
		p_delta_abc = (double)1/2 * (delta1*tau1 + delta2*tau2);
		
		// Kostenwert berechnen.
		k = (double)Math.abs(beta*(l1+l2)*p_delta_abc);
	
		return k;	
	} 
	
	
    /**
     * In dieser Methode wird durch einen Dialog abgefragt, mit 
     * welchem Fehlerwert das Polygon reduziert werden soll. 
     * Mit diesem Wert und dem Eingabebild, wird dann die 
     * implementierte Funktion aufgerufen.
     * 
     * @param ip Ursprungsbild
     */
	public static void Mitzias_Mertzios(final ImageProcessor ip) 
	{	
		// Der Fehlerwert.
		double d_s = 1.0;
		
		// Hier wird der GenericDialog instanziiert, um dem Bentutzer
		// die Eingabe eines Wertes fuer d_s zu ermoeglichen.		
		GenericDialog gd = new GenericDialog("Mitzias und Mertzios");

		// Ein Felder fuer den einzugebenden Parameter wird hinzugefuegt.		
		gd.addNumericField("Ds: ", d_s , 0);

		// Nun wird der Dialog angezeigt.		
		gd.showDialog();
		
		// Wert einlesen.
		d_s = gd.getNextNumber();
		
		// Wird auf abbrechen gedrueckt, wird die Methode beendet.
		if(gd.wasCanceled()) return;
		
		// Konturen extrahieren.
		ContourTracer tracer = new ContourTracer(ip);
		ContourSet cs = tracer.getContours();
		
		// Hier wird die eigentliche Funktion aufgerufen.
		Mitzias_Mertzios_Funktion(ip, d_s, cs.outerContours);
		Mitzias_Mertzios_Funktion(ip, d_s, cs.innerContours);
		
		// Kontur zeichnen.
		Konturzeichnen(ip, cs);
	}
	
	
    /**
     * In dieser Methode wird das Polygon nach dem DCE-Verfahren reduziert.
     * 
     * @param ip Ursprungsbild
     * @param d_s Der Fehlerwert.
     * @param contours Die zu bearbeitende Kontur.  
     */
	public static void Mitzias_Mertzios_Funktion(final ImageProcessor ip, 
			double d_s, ArrayList<Contour> contours) 
	{			
		// Hilfswerte fuer den ersten Algorithmus, um Punkte, die in einer
		// Geraden liegen, vorab zu loeschen.
		int x_i = 0;
		int x_i_last = 0;
		int x_i_next = 0;
		int y_i = 0;
		int y_i_last = 0;
		int y_i_next = 0;
		
		double x_c = 0.0; // Schwerpunkt in x-Richtung.
		double y_c = 0.0; // Schwerpunkt in y-Richtung.
		
		// Hilfswerte, um die Position des Konturpunktes mit dem groessten Wert
		// zu finden.
		double globalMaximum = 0.0;
		double tempMaximum = 0.0;
		int indexMaximum = 0;
		
		// Liste, in der die bestehen bleibenden Konturpunkte gespeichert werden.
		ArrayList<Node> v = new ArrayList<Node>();
			
		// Liste, in der die zu loeschenden Knoten gespeichert werden.
		ArrayList<Node> zuLoeschendeKnoten = new ArrayList<Node>();
					
		// Algorithmus 1: Loeschen aller Konturpunkte, die innerhalb einer Linie 
		// liegen.
		for(int i = 0; i < contours.size(); i++) 
		{				
			// Zuerst die Liste loeschen.
			zuLoeschendeKnoten.clear();
			
			// Der erste Konturpunkt wird besonders behandelt, da auch der 
			// vorherige Knoten  betrachtet wird.
			x_i = contours.get(i).nodes.get(0).x;
			x_i_last = contours.get(i).nodes.get(
					contours.get(i).nodes.size()-1).x;
			x_i_next = contours.get(i).nodes.get(1).x;
			y_i = contours.get(i).nodes.get(0).y;
			y_i_last = contours.get(i).nodes.get(
					contours.get(i).nodes.size()-1).y;
			y_i_next = contours.get(i).nodes.get(1).y;
			
			// Sind die Bedingungen erfuellt, so liegt der Konturpunkt 
			// innerhalb einer Linie und wird als zu loeschender Knoten 
			// gespeichert.
			if((x_i == x_i_last && x_i == x_i_next)
					|| (y_i == y_i_last && y_i == y_i_next)
					|| ((x_i-x_i_last) == (x_i_next-x_i) 
						&& (y_i-y_i_last) == (y_i_next-y_i)))
			{
				zuLoeschendeKnoten.add(contours.get(i).nodes.get(0));
			}
				
			// Innerhalb dieser Schleife werden alle anderen Konturpunkte 
			// behandelt.
			for(int j = 1; j < contours.get(i).nodes.size(); j++) 
			{
				x_i = contours.get(i).nodes.get(j).x;
				x_i_last = contours.get(i).nodes.get(j-1).x;
				x_i_next = contours.get(i).nodes.get(
						(j+1)%contours.get(i).nodes.size()).x;
				y_i = contours.get(i).nodes.get(j).y;
				y_i_last = contours.get(i).nodes.get(j-1).y;
				y_i_next = contours.get(i).nodes.get(
						(j+1)%contours.get(i).nodes.size()).y;
				
				// Sind die Bedingungen erfuellt, so liegt der Konturpunkt 
				// innerhalb einer Linie und wird als zu loeschender Knoten 
				// gespeichert.
				if((x_i == x_i_last && x_i == x_i_next)
						|| (y_i == y_i_last && y_i == y_i_next)
						|| ((x_i-x_i_last) == (x_i_next-x_i) 
							&& (y_i-y_i_last) == (y_i_next-y_i)))
				{
					zuLoeschendeKnoten.add(contours.get(i).nodes.get(j));
				}
			}
			
			// In dieser Schleife werden die gespeicherten Knoten geloescht.
			for(int j = 0; j < zuLoeschendeKnoten.size(); j++) 
			{			
				contours.get(i).nodes.remove(zuLoeschendeKnoten.get(j));
			}
		}
			
		// Algortithmus 2: Ab hier werden weitere Konturpunkte anhand der 
		// euklidischen Distanz entfernt.
		for(int i = 0; i < contours.size(); i++) 
		{					
			// Hier wird der Schwerpunkt ermittelt. Zuerst muessen in dieser
			// Schleife alle Konturpunkte in x- und y-Richtung aufaddiert werden.
			for(int j = 0; j < contours.get(i).nodes.size(); j++) 
			{
				x_c += contours.get(i).nodes.get(j).x;
				y_c += contours.get(i).nodes.get(j).y;
			}
			
			// Hier wird der Mittelwert gebildet. Das Ergebnis ist dann der 
			// Schwerpunkt der Kontur.
			x_c = (double)(x_c/contours.get(i).nodes.size());
			y_c = (double)(y_c/contours.get(i).nodes.size());
			
			// Nun wird der Startpunkt fuer die nachfolgende Polygonisierung berechnet. 
			// Es wird der Punkt gewaehlt, der die groesste euklidische Distanz zum 
			// Schwerpunkt der Kontur besitzt.
			for(int j = 0; j < contours.get(i).nodes.size(); j++) 
			{
				// Euklidische Distanz des aktuellen Punktes zwischenspeichern.
				tempMaximum = (double)Math.sqrt(
						(double)Math.pow(x_c-contours.get(i).nodes.get(j).x, 2) 
						+ (double)Math.pow(y_c-contours.get(i).nodes.get(j).y, 2));
				
				// Beim ersten Punkt wird der Wert als globales Maximum gespeichert 
				// und die Indexposition des Knoten. Bei allen anderen Knoten muss 
				// geprueft werden, ob der Wert groesser ist, als das globale Maximum.
				if(j == 0)
				{
					globalMaximum = tempMaximum;						
					indexMaximum = j;
				} else {
					if(Double.compare(tempMaximum, globalMaximum) > 0)
					{
						globalMaximum= tempMaximum;					
						indexMaximum = j;
					}
				}
			}
			
			// Alle Elemente vor dem Starpunkt hinten anhaengen.
			for(int j = 0; j < indexMaximum; j++) 
			{
				contours.get(i).nodes.add(contours.get(i).nodes.get(0));
				contours.get(i).nodes.remove(0);
			}
				
			// Startpunkt als bleibender Konturpunkt speichern.
			v.add(contours.get(i).nodes.get(0));
			
			// Index fuer ein bestehen bleibendender Konturpunkt.
			int indexV = 0;
			
			// Start-Index des Punktes, mit dem der Punkt an indexV
			// eine Gerade bildet.
			int j = 2;
			
			// Innerhalb dieser Schleife wird der Index des Endpunktes,
			// welcher mit dem Starpunkt eine Gerade bildet, erhoeht.
			while(j < contours.get(i).nodes.size())
			{
				// In dieser Schleife wird der Index der Konturpunkte zwischen
				// Start- und Endpunkt der Geraden erhoet.
				for(int k = indexV+1; k < j; k++)
				{
					// Fehlerwert fuer einen Punkt zwischen Start- und Endpunkt der
					// Geraden.
					tempMaximum = Euklidische_Distanz(contours.get(i).nodes.get(indexV), 
							contours.get(i).nodes.get(j), 
							contours.get(i).nodes.get(k));
					
					// Beim ersten Punkt wird der Wert als globales Maximum gespeichert 
					// und die Indexposition des Knoten. Bei allen anderen Knoten muss 
					// geprueft werden, ob der Wert groesser ist, als das globale Maximum.
					if(k == indexV+1)
					{
						globalMaximum = tempMaximum;						
						indexMaximum = k;
					} else {
						if(Double.compare(tempMaximum, globalMaximum) > 0)
						{
							globalMaximum = tempMaximum;					
							indexMaximum = k;
						}
					}
				}
						
				// Ist der groesste Fehler groesser als der definierte Wert,
				// so handelt es sich bei dem Punkt um einen wichtigen 
				// Konturpunkt. Der Punkt wird gespeichert und fuer weitere
				// Berechnungen als Startpunkt gespeichert. Der neue Endpunkt
				// der Geraden ist der uebernaechste Punkt.
				// Ansonsten wird nur der Index des Endpunktes erhoeht.
				if(Double.compare(globalMaximum, d_s) > 0)
				{
					v.add(contours.get(i).nodes.get(indexMaximum));
					indexV = indexMaximum;		
					j = indexV+2;
				} else {
					j++;
				}
			} 
					
			// Ist der indexV nicht bis ans Ende der Liste geschoben worden, so wird 
			// hier noch mal eine Gerade mit dem ersten Konturpunkt gebildet. 
			if(indexV != contours.get(i).nodes.size()-1)
			{
				// In dieser Schleife wird der Index der Konturpunkte zwischen
				// Start- und Endpunkt der Geraden erhoet.
				for(int k = indexV+1; k < contours.get(i).nodes.size(); k++)
				{
					// Fehlerwert fuer einen Punkt zwischen Start- und Endpunkt der
					// Geraden.
					tempMaximum = Euklidische_Distanz(contours.get(i).nodes.get(indexV), 
							contours.get(i).nodes.get(0), 
							contours.get(i).nodes.get(k));
					
					// Beim ersten Punkt wird der Wert als globales Maximum gespeichert 
					// und die Indexposition des Knoten. Bei allen anderen Knoten muss 
					// geprueft werden, ob der Wert groesser ist, als das globale Maximum.
					if(k == indexV+1)
					{
						globalMaximum = tempMaximum;						
						indexMaximum = k;
					} else {
						if(Double.compare(tempMaximum, globalMaximum) > 0)
						{
							globalMaximum= tempMaximum;					
							indexMaximum = k;
						}
					}
				}
					
				// Ist der groesste Fehler groesser als der definierte Wert,
				// so handelt es sich bei dem Punkt um einen wichtigen 
				// Konturpunkt. Der Punkt wird gespeichert.
				if(Double.compare(globalMaximum, d_s) > 0)
				{
					v.add(contours.get(i).nodes.get(indexMaximum));
				}
			}
			
			// Hier die alten Konturpunkte loeschen.
			contours.get(i).nodes.clear();
			
			// Tiefe Kopie der bleibenden Konturpunkte.
			for(int k = 0; k < v.size(); k++)
			{
				contours.get(i).nodes.add(new Node(v.get(k).x, v.get(k).y));
			}
		}
	} 
	
	
    /**
     * Die Funktion zur Berechnung der euklidischen Distanz 
     * fuer das MM-Verfahren.
     * 
     * @param pi Der Startpunkt der Geraden.
     * @param pj Der Endpunkt der Geraden.
     * @param pk Der Punkt fuer den die Distanz berechnet werden soll.
     * 
     * @return Die euklidische Distanz.
     */
	public static double Euklidische_Distanz(Node pi, Node pj, Node pk) 
	{
		// Die euklidische Distanz.
		double e_ij_k = 0.0;
		
		// Laenge der drei Geraden.
		double d_p_ij = 0.0;
		double d_p_ik = 0.0;
		double d_p_jk = 0.0;
		
		// Der Winkel zwischen Punkt j und k.
		double theta_jk_i = 0.0;
		
		// Lanegen der drei Geraden, die mit den drei Punkten gebildet werden koennen.
		d_p_ij = (double)Math.sqrt((double)Math.pow(pi.x-pj.x, 2) 
				+ (double)Math.pow(pi.y-pj.y, 2));
		d_p_ik = (double)Math.sqrt((double)Math.pow(pi.x-pk.x, 2) 
				+ (double)Math.pow(pi.y-pk.y, 2));
		d_p_jk = (double)Math.sqrt((double)Math.pow(pj.x-pk.x, 2) 
				+ (double)Math.pow(pj.y-pk.y, 2));
		
		// Winkel berechnen.
		theta_jk_i = (double)Math.acos((double)(((double)Math.pow(d_p_ik, 2)
				+(double)Math.pow(d_p_ij, 2)
				-(double)Math.pow(d_p_jk, 2)) / (2*d_p_ik*d_p_ij)));
		
		//  Euklidische Distanz berechnen.
		e_ij_k = d_p_ik*(double)Math.sin(theta_jk_i);
			
		return e_ij_k;		
	} 
	
	
	/**
	 * Funktion, die die Lage des Punktes c im Bezug zur Geraden, 
	 * die durch die Punkte a und b gebildet wird, bestimmt.
	 * 
	 * @param a Der Startpunkt der Geraden.
	 * @param b Der Endpunkt der Geraden.
	 * @param c Der Punkt fuer den die Lage berechnet werden soll.
	 * 
	 * @return Die Lage des Punktes c in Bezug zur Geraden a_b.
	 */
	public static Enum<Punktlage> Punktlage(Node a, Node b, Node c) 
	{				
		// Der Winkel, um den die drei Punkte rotiert werden muessen,
		// damit das Doppelkreuz senkrecht im Bild steht und der Punkt
		// a unten im Bild ist und der Punkt b oben.
		double rotationsWinkel = 0.0;
		
		// Die neuen Positionen der drei Punkte nach der Rotation.
		float a_x = 0;
		float a_y = 0;
		float b_x = 0;
		float b_y = 0;
		float c_x = 0;
		float c_y = 0;
		
		// Liegt Punkt c mt Punkt a uebereinander, so kann dies
		// direkt ausgegeben werden.
		if(c.x == a.x && c.y == a.y)
		{
			return Enum.valueOf(Punktlage.class, "PUNKT_A");
		}
		
		// Liegt Punkt c mt Punkt b uebereinander, so kann dies
		// direkt ausgegeben werden.
		if(c.x == b.x && c.y == b.y)
		{
			return Enum.valueOf(Punktlage.class, "PUNKT_B");
		}
		
		// Ist das Doppelkreuz noch nicht senkrecht, so muss
		// hier der Winkel zur Rotation der Punkte berechnet werden.
		if(a.x != b.x)
		{
			// Je nachdem ob a rechts oder links von b ist,
			// muss rechts bzw. links rotiert werden.
			if(a.x > b.x)
			{
				double steigung = (double)(a.y-b.y)/(a.x-b.x);
				rotationsWinkel = ((double)(Math.PI/2)
						-(double)Math.atan(steigung));					
			} else {
				double steigung = (double)(a.y-b.y)/(a.x-b.x);
				rotationsWinkel = (-1)*((double)(Math.PI/2)
						+(double)Math.atan(steigung));
			}
		} else {
			// Ist der Punkt a oben im Bild, so muss um 180 Grad
			// gedreht werden.
			if(a.y < b.y)
			{
				rotationsWinkel = (double)Math.PI;
			}
		}
		
		// Hier werden die Positionen der rotierten Punkte berechnet.
		a_x = (float) ((double)Math.cos(rotationsWinkel)*a.x 
				- (double)Math.sin(rotationsWinkel)*a.y);
		a_y = (float) ((double)Math.sin(rotationsWinkel)*a.x 
				+ (double)Math.cos(rotationsWinkel)*a.y);
		
		b_x = (float) ((double)Math.cos(rotationsWinkel)*b.x 
				- (double)Math.sin(rotationsWinkel)*b.y);
		b_y = (float) ((double)Math.sin(rotationsWinkel)*b.x 
				+ (double)Math.cos(rotationsWinkel)*b.y);
		
		c_x = (float) ((double)Math.cos(rotationsWinkel)*c.x 
				- (double)Math.sin(rotationsWinkel)*c.y);
		c_y = (float) ((double)Math.sin(rotationsWinkel)*c.x 
				+ (double)Math.cos(rotationsWinkel)*c.y);
			
		// Ab hier finden alle anderen Lagevergleiche statt.
		
		if(Float.compare(a_y, c_y) == 0 && Float.compare(a_x, c_x) > 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_VON_A");	
		}
		
		if(Float.compare(a_y, c_y) == 0 && Float.compare(a_x, c_x) < 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_VON_A");
		}
		
		if(Float.compare(b_y, c_y) == 0 && Float.compare(b_x, c_x) > 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_VON_B");	
		}
		
		if(Float.compare(b_y, c_y) == 0 && Float.compare(b_x, c_x) < 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_VON_B");
		}
		
		if(Float.compare(a_x, c_x) == 0 && Float.compare(a_y, c_y) < 0)
		{
			return Enum.valueOf(Punktlage.class, "HINTEN");
		}
		
		if(Float.compare(b_x, c_x) == 0 && Float.compare(b_y, c_y) > 0)
		{
			return Enum.valueOf(Punktlage.class, "VORNE");
		}
		
		if(Float.compare(a_x, c_x) == 0 && Float.compare(b_y, c_y) < 0 
				&& Float.compare(a_y, c_y) > 0)
		{
			return Enum.valueOf(Punktlage.class, "ZWISCHEN_A_UND_B");
		}
		
		if(Float.compare(c_y, b_y) < 0 && Float.compare(c_x, b_x) < 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_VORNE");
		}
		
		if(Float.compare(c_y, b_y) < 0 && Float.compare(c_x, b_x) > 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_VORNE");
		}
			
		if(Float.compare(c_y, a_y) > 0 && Float.compare(c_x, a_x) < 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_HINTEN");
		}
		
		if(Float.compare(c_y, a_y) > 0 && Float.compare(c_x, a_x) > 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_HINTEN");
		}
			
		if(Float.compare(c_y, a_y) < 0 && Float.compare(c_y, b_y) > 0 
				&& Float.compare(c_x, a_x) < 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_ZWISCHEN_A_UND_B");
		}
		
		if(Float.compare(c_y, a_y) < 0 && Float.compare(c_y, b_y) > 0 
				&& Float.compare(c_x, a_x) > 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B");
		}
		
		return Enum.valueOf(Punktlage.class, "ERROR");
	} 
	
	
    /**
     * Funktion, die die Lage der Gerade c_d im Bezug zur Geraden, 
     * die durch die Punkte a und b gebildet wird, bestimmt.
     * 
     * @param a Der Startpunkt der Bezugs-Geraden.
     * @param b Der Endpunkt der Bezugs-Geraden.
     * @param c Der Startpunkt der Geraden.
     * @param d Der Endpunkt der Geraden.
     * 
     * @return Die Lage der Geraden c_d in Bezug zur Geraden a_b.
     */
	public static Enum<BA23_Relation> BA23_Relation(Node a, Node b, 
			Node c, Node d) 
	{				
		// Enum's um die Lage der beiden Punkte
		// c und d zu speichern.
		Enum<Punktlage> punktRelation_C;
		Enum<Punktlage> punktRelation_D;
		
		// Lage der Punkte c und d im Bezug zur Geraden a_b.
		punktRelation_C = Punktlage(a, b, c);
		punktRelation_D = Punktlage(a, b, d);
			
		// An hier wird die BA23 Relation bestimmt.
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "PUNKT_A")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "PUNKT_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "PUNKT_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "PUNKT_A")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "ID");
		}
		
		// Wenn die Gerade nicht die ID ist, so werden hier die 
		// Grenzfaelle behandelt.
		punktRelation_C = Punktlagenkonvertierung(punktRelation_C);
		punktRelation_D = Punktlagenkonvertierung(punktRelation_D);
		
		// Nach der Konvertierung koennen die anderen Relationen 
		// behandelt werden.
		
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_VORNE")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_LEFT");
		}
		
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_VORNE")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_VORNE")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_VORNE")) == 0
					&& punktRelation_D.compareTo(Enum.valueOf(
							Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_MIDDLE");
		}
				
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_LEFT");
		}
				
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_MIDDLE_LEFT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_OVERLAPS_MIDDLE_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_CONTAINS_LEFT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "FRONT_CONTAINS_RIGHT");
		}
		
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "DURING_LEFT");
		}
		
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "DURING_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "CONTAINS_LEFT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "CONTAINS_RIGHT");
		}
					
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_CONTAINS_LEFT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_VORNE")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_VORNE")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_CONTAINS_RIGHT");
		}
				
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_HINTEN")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_LEFT");
		}
				
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_HINTEN")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "RECHTS_HINTEN")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_MIDDLE_LEFT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B")) == 0
						&& punktRelation_D.compareTo(Enum.valueOf(
								Punktlage.class, "LINKS_HINTEN")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_OVERLAPS_MIDDLE_RIGHT");
		}
			
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "LINKS_HINTEN")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_LEFT");
		}
		
		if(punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "RECHTS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0)
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_RIGHT");
		}
		
		if((punktRelation_C.compareTo(Enum.valueOf(
				Punktlage.class, "LINKS_HINTEN")) == 0
				&& punktRelation_D.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0)
				|| (punktRelation_C.compareTo(Enum.valueOf(
						Punktlage.class, "RECHTS_HINTEN")) == 0
					&& punktRelation_D.compareTo(Enum.valueOf(
							Punktlage.class, "LINKS_HINTEN")) == 0))
		{
			return Enum.valueOf(BA23_Relation.class, "BACK_MIDDLE");
		}

		return Enum.valueOf(BA23_Relation.class, "_");
	} 
	
	
    /**
     * Da die Lage eines Punktes auf dem Punkt A, Punkt B
     * oder einem der Geradenabschnitte im Bezug zur
     * BA23 Relation mehrdeutig ist, wird hier die Position
     * des Punktes einem der sechs Bereiche zugeordnet.
     * 
     * @param relation Die vorherige Relation des Punktes.
     * 
     * @return Die veraenderte Lage des Punktes.
     */
	public static Enum<Punktlage> Punktlagenkonvertierung(Enum<Punktlage> relation) 
	{		
		// Ab hier wird die Lage des Punktes umdefiniert.
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "PUNKT_A")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_HINTEN");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "PUNKT_B")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_VORNE");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "VORNE")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_VORNE");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "HINTEN")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_HINTEN");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "LINKS_VON_B")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_ZWISCHEN_A_UND_B");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "RECHTS_VON_B")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "LINKS_VON_A")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_ZWISCHEN_A_UND_B");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "RECHTS_VON_A")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "RECHTS_ZWISCHEN_A_UND_B");
		}
		
		if(relation.compareTo(Enum.valueOf(Punktlage.class, "ZWISCHEN_A_UND_B")) == 0)
		{
			return Enum.valueOf(Punktlage.class, "LINKS_ZWISCHEN_A_UND_B");
		}
		
		// Liegt der Punkt in einem der sechs Bereiche, so wird die Lage
		// nicht veraendert.
		return relation;	
	}
	
	
    /**
     * Diese Methode sorgt dafuer, dass die uebergebende Kontur
     * vereinfacht wird. Innere und auessere Konturen
     * werden mit den gleichen Parametern bearbeitet.
     * 
     * @param ip Ursprungsbild
     * @param cs Die zu bearbeitenden Konturen.
     */
	public static void Konturvereinfachung(final ImageProcessor ip, ContourSet cs) 
	{	
		// Werte fuer DCE und MM.
		int sollPunkte = 10;
		double d_s = 1.0;
						
		// Hier wird der GenericDialog instanziiert, um dem Bentutzer
		// die Eingabe eines Wertes zu ermoeglichen.		
		GenericDialog gd = new GenericDialog("Konturvereinfachung");

		// Ein Felder fuer den einzugebenden Parameter wird hinzugefuegt.		
		gd.addNumericField("Anzahl Konturpunkte: ", sollPunkte , 0);
		
		// Ein Felder fuer den einzugebenden Parameter wird hinzugefuegt.		
		gd.addNumericField("Ds: ", d_s , 0);
		
		// Auswahl des zu verwendenden Verfahren.
		String[] items = new String[2];
		items[0] = "DCE";
		items[1] = "MM";
		
		gd.addChoice("Polygonisierungsverfahren: ", items, "DCE");
		
		// Nun wird der Dialog angezeigt.		
		gd.showDialog();
		
		// Werte einlesen.
		sollPunkte = (int) gd.getNextNumber();
		d_s = gd.getNextNumber();
		
		// Wird auf abbrechen gedrueckt, wird die Methode beendet.
		if(gd.wasCanceled()) return;
				
		// Je nach Auswahl wird die Kontur vereinfacht.
		if(gd.getNextChoice().compareTo("DCE") == 0)
		{
			Discrete_Curve_Evolution_Funktion(ip, sollPunkte, cs.outerContours);
			Discrete_Curve_Evolution_Funktion(ip, sollPunkte, cs.innerContours);
		} else {
			Mitzias_Mertzios_Funktion(ip, d_s, cs.outerContours);
			Mitzias_Mertzios_Funktion(ip, d_s, cs.innerContours);
		}
	}
	
	
    /**
     * Diese Methode vereinfacht sortiert die Konturpunkte so, 
     * dass der Punkt oben links im Bild, der erste Punkt in der 
     * Konturliste ist. Die angrenzenden Konturpunkte werden gegen 
     * den Uhrzeigersinn gespeichert.
     * 
     * @param ip Ursprungsbild
     * @param contours Die zu bearbeitende Kontur.  
     */
	public static void Kontursortierung(final ImageProcessor ip, 
			ArrayList<Contour> contours, boolean outerFlag) 
	{	
		// Position des ganz oben links im Bild liegenden
		// Konturpunktes.
		int minX = 0;
		int minY = 0;
		int indexMinimum = 0;
		
		// In diese Liste wird die neue Reihenfolge gespeichert.
		ArrayList<Node> tempNodes = new ArrayList<Node>();
							
		// Hier werden die Konturpunkte umsortiert.
		for(int i = 0; i < contours.size(); i++) 
		{
			// In dieser Schleife wird die Position des ganz oben links
			// im Bild liegenden Punktes bestimmt.
			for(int j = 0; j < contours.get(i).nodes.size(); j++) 
			{
				if(j == 0)
				{
					minX = contours.get(i).nodes.get(j).x;
					minY = contours.get(i).nodes.get(j).y;	
					indexMinimum = j;
				} else {
					if(minY > contours.get(i).nodes.get(j).y)
					{
						minX = contours.get(i).nodes.get(j).x;
						minY = contours.get(i).nodes.get(j).y;	
						indexMinimum = j;
					}
				}
			}
			
			// Zuerst die Liste loeschen.
			tempNodes.clear();
			
			// Je nachdem, ob es sich um auessere oder innere Konturen
			// handelt, muss unterschiedlich sortiert werden.
			if(outerFlag)
			{
				// Alle Elemente vom Startpunkt bis zum ersten Element der Liste
				// speichern.
				for(int j = indexMinimum; j >= 0 ; j--) 
				{
					tempNodes.add(contours.get(i).nodes.get(j));
				}
				
				// Alle Elemente vom Ende der Liste bis zum Startpunkt speichern.
				for(int j = contours.get(i).nodes.size()-1; j > indexMinimum ; j--) 
				{
					tempNodes.add(contours.get(i).nodes.get(j));
				}
			} else {
				// Alle Elemente vom Startpunkt bis zum ersten Element der Liste
				// speichern.
				for(int j = indexMinimum; j < contours.get(i).nodes.size() ; j++) 
				{
					tempNodes.add(contours.get(i).nodes.get(j));
				}
				
				// Alle Elemente vom Ende der Liste bis zum Startpunkt speichern.
				for(int j = 0; j < indexMinimum; j++) 
				{
					tempNodes.add(contours.get(i).nodes.get(j));
				}
			}
			
			// Hier die alte Sortierung der Konturpunkte loeschen.
			contours.get(i).nodes.clear();
			
			// Tiefe Kopie der neuen Sortierung der Konturpunkte.
			for(int k = 0; k < tempNodes.size(); k++)
			{
				contours.get(i).nodes.add(new Node(tempNodes.get(k).x, 
						tempNodes.get(k).y));
			}
		}
	}
}
